package com.jwgf.queueprogramflash;

import com.jwgf.can.CanMessage;
import com.jwgf.comlibrary.CANManager;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.functions.Consumer;


public class FlashManager {
    private static FlashManager instance;

    private ExecutorService executorService;

    private List<HexPage> hexPages;

    private List<Integer> updateSpindles;
    private List<Integer> bootEchoSpindles;
    private List<Integer> jumpBootSucceedAndUpdateNode;
    private List<Integer> eraseEchoSpindles;
    private List<Integer> eraseSucceedSpindles;
    private List<Integer> flashSucceedSpindles;
    private List<Integer> userEchoSpindles;

    private List<byte[]> echoList;

    private boolean isAllSucceed;

    private FlashManager() {
        echoList = new ArrayList<>();
        executorService = Executors.newSingleThreadExecutor();
        EventBus.getDefault().register(this);
    }

    public static FlashManager getInstance() {
        if (instance == null) {
            instance = new FlashManager();
        }
        return instance;
    }

    public void send(byte[] data) {
        int canId = (data[0] << 8) | (data[1] & 0xff);
        byte[] sendData = new byte[8];
        System.arraycopy(data, 2, sendData, 0, data.length - 2);
        CANManager.getInstance().write1(canId, sendData);
        try {
            Thread.sleep(8);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Subscribe
    public void onCanMessage(CanMessage msg) {
        byte[] data = msg.getData();
        int canId = data[1] & 0xff;
        if (canId >= 2 && canId <= 73) {
            addEcho(data);
        }
    }

    public synchronized void addEcho(byte[] data) {
        echoList.add(data);
    }

    public synchronized List<byte[]> getEcho() {
        List<byte[]> echo = new ArrayList<>(echoList);
        echoList.clear();
        return echo;
    }

    public List<Integer> getEchoSpindles() {
        List<byte[]> echo = getEcho();
        List<Integer> spindles = new ArrayList<>();
        for (byte[] data : echo) {
            spindles.add(data[1] & 0xff);
        }
        return spindles;
    }

    public int getPageCnt() {
        return hexPages.size();
    }

    public void startFlash(String name, List<Integer> updateSpindles, FlashStateListener listener) {
        isAllSucceed = false;
        // 解析文件
        publishFlashState(listener, "开始解析hex文件...");
        HexPageCallable callable = new HexPageCallable(name, 8 * 1024);
        this.updateSpindles = updateSpindles;
        Future<List<HexPage>> future = executorService.submit(callable);
        try {
            this.hexPages = future.get();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 跳转bootloader
        if (hexPages != null) {
            publishFlashState(listener, "解析hex文件成功");
            JumpBootCallable bootCallable = new JumpBootCallable(updateSpindles);
            Future<List<Integer>> bootFuture = executorService.submit(bootCallable);
            try {
                bootEchoSpindles = bootFuture.get();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            publishFlashState(listener, "解析hex文件失败");
            return;
        }

        List<Integer> spindlesShow = new ArrayList();

        // 跳转成功且升级的单锭，发送擦除命令
        if(bootEchoSpindles != null) {
            for (Integer spindle : bootEchoSpindles) {
                spindlesShow.add(spindle - 1);
            }
            publishFlashState(listener, "跳转bootloader成功的单锭:" + spindlesShow.toString());
        }
        jumpBootSucceedAndUpdateNode = new ArrayList<>(bootEchoSpindles);
        jumpBootSucceedAndUpdateNode.retainAll(updateSpindles);
        if (!jumpBootSucceedAndUpdateNode.isEmpty()) {
            StartEraseCallable startEraseCallable = new StartEraseCallable(jumpBootSucceedAndUpdateNode);
            Future<List<Integer>> eraseEchoFuture = executorService.submit(startEraseCallable);
            try {
                eraseEchoSpindles = eraseEchoFuture.get();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            spindlesShow.clear();
            for (Integer spindle : updateSpindles) {
                spindlesShow.add(spindle - 1);
            }
            publishFlashState(listener, "需要升级跳转失败的单锭" + spindlesShow.toString());
            return;
        }

        // 查询擦除结果
        if (!eraseEchoSpindles.isEmpty()) {
            spindlesShow.clear();
            for (Integer spindle : eraseEchoSpindles) {
                spindlesShow.add(spindle - 1);
            }
            publishFlashState(listener, "回应擦除的单锭:" + spindlesShow.toString());
            EraseQueryCallable eraseQueryCallable = new EraseQueryCallable(eraseEchoSpindles);
            Future<List<Integer>> eraseQueryFuture = executorService.submit(eraseQueryCallable);

            try {
                eraseSucceedSpindles = eraseQueryFuture.get();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            publishFlashState(listener, "没有单锭回复擦除命令");
            return;
        }

        // 开始按页烧写
        if (!eraseSucceedSpindles.isEmpty()) {
            spindlesShow.clear();
            for (Integer spindle : eraseSucceedSpindles) {
                spindlesShow.add(spindle - 1);
            }
            publishFlashState(listener, "擦除成功的单锭:" + spindlesShow.toString());
            FlashPageCallable pageCallable = new FlashPageCallable(hexPages, eraseSucceedSpindles);
            Future<List<Integer>> flashSucceedFuture = executorService.submit(pageCallable);
            try {
                flashSucceedSpindles = flashSucceedFuture.get();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            publishFlashState(listener, "没有单锭擦除成功");
            return;
        }

        // 跳转到User
        if (!flashSucceedSpindles.isEmpty()) {
            spindlesShow.clear();
            for (Integer spindle : flashSucceedSpindles) {
                spindlesShow.add(spindle - 1);
            }
            publishFlashState(listener, "升级成功的单锭:" + spindlesShow.toString());
            JumpUserCallable userCallable = new JumpUserCallable();
            Future<List<Integer>> jumpUserFuture = executorService.submit(userCallable);

            try {
                userEchoSpindles = jumpUserFuture.get();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            publishFlashState(listener, "所有升级失败");
            return;
        }

        if (flashSucceedSpindles.equals(updateSpindles)) {
            isAllSucceed = true;
        }

    }

    public boolean isAllSucceed() {
        return isAllSucceed;
    }

    private void publishFlashState(FlashStateListener listener, String msg) {
        Observable.just(false).observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Boolean>() {
                    @Override
                    public void accept(Boolean aBoolean) throws Exception {
                        if (listener != null) {
                            listener.onFlashStateChanged(msg);
                        }
                    }
                });
    }

    public static interface FlashStateListener {
        void onFlashStateChanged(String msg);
    }
}
